package com.agua.enturno.vista.util;

import com.agua.enturno.modelo.turno.EstadoDeTurno;
import com.agua.enturno.util.Configuration;
import com.agua.enturno.vista.dto.DiaDTO;
import com.agua.enturno.vista.dto.HorarioDTO;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Interval;
import org.joda.time.LocalDate;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Clase de utilidad a la hora de dibujar una agenda.
 * 
 * @author Victor Del Rio
 *
 */
public class AgendaUtil {

	private static final Duration UTILITY_DURATION = Duration.standardMinutes(30);
	
	
	/*------------------------------------ Public methods ----------------------------------*/

	public DiaDTO rellenarDia(DiaDTO dia) {
		
		List<HorarioDTO> horariosDeRelleno = this.getHorariosDeRelleno(dia.getFecha());
		
		this.rellenarConDuracionPorDefecto(dia.getHorarios(), horariosDeRelleno);
		this.completarElRelleno(dia.getHorarios(), dia.getFecha());
			
		return dia;
	}

	/*--------------------------------------------------------------------------------------*/
	/*----------------------------------- Private methods ----------------------------------*/

	private List<HorarioDTO> getHorariosDeRelleno(LocalDate dia) {
		
		List<HorarioDTO> horariosDeRelleno = new ArrayList<HorarioDTO>();
		DateTime inicioDelHorario = dia.toDateTimeAtStartOfDay();
		
		do {
			
			Interval intervaloDelHorario = new Interval(inicioDelHorario, Configuration.getInstance().getDuracionDefectoDeHorarios());
			HorarioDTO horario = new HorarioDTO(intervaloDelHorario, EstadoDeTurno.INEXISTENTE);
			horariosDeRelleno.add(horario);
			
			inicioDelHorario = inicioDelHorario.plus(Configuration.getInstance().getDuracionDefectoDeHorarios());
			
		} while ( inicioDelHorario.isBefore(dia.plusDays(1).toDateTimeAtStartOfDay()) );
		
		return horariosDeRelleno;
	}
	
	private void rellenarConDuracionPorDefecto(List<HorarioDTO> horarios,  List<HorarioDTO> horariosDeRelleno) {
		
		for (HorarioDTO horarioDeRelleno : horariosDeRelleno) {
			
			if ( sePuedeInsertarRelleno(horarios, horarioDeRelleno) ) 
				horarios.add(horarioDeRelleno);
		}
		
		Collections.sort(horarios);
	}
	
	private boolean sePuedeInsertarRelleno(List<HorarioDTO> horarios, final HorarioDTO horarioDeRelleno) {
		
		return !Iterables.any(horarios, new Predicate<HorarioDTO>() {
			@Override
			public boolean apply(HorarioDTO element) {
				return element.getIntervalo().overlaps(horarioDeRelleno.getIntervalo());
			}
		});
	}
	
	private void completarElRelleno(List<HorarioDTO> horarios, LocalDate dia) {
		
		List<HorarioDTO> horariosDeRelleno = new ArrayList<HorarioDTO>();
		
		this.agregarInicioYFinDelDia(horarios, dia);
				
		Iterator<HorarioDTO> itHorarios = horarios.iterator();
		HorarioDTO horarioAnterior = itHorarios.next();
		
		while ( itHorarios.hasNext() ) {
			
			HorarioDTO horarioActual = itHorarios.next();
			
			DateTime finDelHorarioAnterior = horarioAnterior.getIntervalo().getEnd();
			DateTime inicioDelHorarioActual = horarioActual.getIntervalo().getStart();
			
			if ( finDelHorarioAnterior.isBefore(inicioDelHorarioActual) ) {
				
				HorarioDTO horario = new HorarioDTO(new Interval(finDelHorarioAnterior, inicioDelHorarioActual), EstadoDeTurno.INEXISTENTE);
				horariosDeRelleno.add(horario);
			}
			
			horarioAnterior = horarioActual;
		}
		
		horarios.addAll(horariosDeRelleno);
		Collections.sort(horarios);
		
		this.quitarInicioYFinDelDia(horarios);
	}

	private void agregarInicioYFinDelDia(List<HorarioDTO> horarios, LocalDate dia) {
		
		DateTime inicioDelDia = dia.toDateTimeAtStartOfDay();
		DateTime finDelDia = dia.plusDays(1).toDateTimeAtStartOfDay();
				
		horarios.add(new HorarioDTO(new Interval(UTILITY_DURATION, inicioDelDia), EstadoDeTurno.INEXISTENTE));
		horarios.add(new HorarioDTO(new Interval(finDelDia, UTILITY_DURATION), EstadoDeTurno.INEXISTENTE));
		
		Collections.sort(horarios);
	}
	
	private void quitarInicioYFinDelDia(List<HorarioDTO> horarios) {
		
		horarios.remove(0);
		horarios.remove(horarios.size()-1);
	}

	/*--------------------------------------------------------------------------------------*/

}
